/*
 * Copyright 2010-2018 Eric Kok et al.
 *
 * Transdroid is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Transdroid is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Transdroid.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.transdroid.core.gui.navigation;

import android.content.Context;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ListView;

import org.transdroid.core.gui.navigation.SelectionModificationSpinner.OnModificationActionSelectedListener;
import org.transdroid.daemon.Finishable;

/**
 * A helper to implement {@link ListView} selection modification behaviour with the {@link SelectionModificationSpinner}
 * by implementing the specific actions and providing a title based on the number of currently selected items. It is
 * important that the provided list was instantiated already.
 *
 * @author Eric Kok
 */
public class SelectionManagerMode implements MultiChoiceModeListener, OnModificationActionSelectedListener {

    private final Context themedContext;
    private final ListView managedList;
    private final int titleTemplateResource;
    private Class<?> onlyCheckClass = null;

    /**
     * Instantiates the helper by binding it to a specific {@link ListView} and providing the text resource to display
     * as title in the spinner.
     *
     * @param themedContext         The context which is associated with the correct theme to apply when inflating views, i.e. the toolbar context
     * @param managedList           The list to manage the selection for and execute selection action to
     * @param titleTemplateResource The string resource id to show as the spinners title; the number of selected items
     *                              will be supplied as numeric formatting argument
     */
    public SelectionManagerMode(Context themedContext, ListView managedList, int titleTemplateResource) {
        this.themedContext = themedContext;
        this.managedList = managedList;
        this.titleTemplateResource = titleTemplateResource;
    }

    /**
     * Set the class type of items that are allowed to be checked in the {@link ListView}. Defaults to null, which means
     * every list view row can be checked.
     *
     * @param onlyCheckClass The {@link Class} instance to use to check list item types against
     */
    public void setOnlyCheckClass(Class<?> onlyCheckClass) {
        this.onlyCheckClass = onlyCheckClass;
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        // Allow modification of selection through a spinner
        SelectionModificationSpinner selectionSpinner = new SelectionModificationSpinner(themedContext);
        selectionSpinner.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        selectionSpinner.setOnModificationActionSelectedListener(this);
        mode.setCustomView(selectionSpinner);
        return true;
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false;
    }

    @Override
    public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
        int checkedCount = 0;
        for (int i = 0; i < managedList.getCheckedItemPositions().size(); i++) {
            if (managedList.getCheckedItemPositions().valueAt(i)
                    && (onlyCheckClass == null || onlyCheckClass.isInstance(managedList.getItemAtPosition(managedList
                    .getCheckedItemPositions().keyAt(i)))))
                checkedCount++;
        }
        ((SelectionModificationSpinner) mode.getCustomView()).updateTitle(themedContext.getResources()
                .getQuantityString(titleTemplateResource, checkedCount, checkedCount));
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        return false;
    }

    /**
     * Implements the {@link SelectionModificationSpinner}'s invert selection command by flipping the checked status for
     * each (enabled) items in the {@link ListView}.
     */
    @Override
    public void invertSelection() {
        SparseBooleanArray checked = managedList.getCheckedItemPositions();
        for (int i = 0; i < managedList.getAdapter().getCount(); i++) {
            if (managedList.getAdapter().isEnabled(i)
                    && (onlyCheckClass == null || onlyCheckClass.isInstance(managedList.getItemAtPosition(i))))
                managedList.setItemChecked(i, !checked.get(i, false));
        }
    }

    /**
     * Implements the {@link SelectionModificationSpinner}'s select all command by checking each (enabled) item in the
     * {@link ListView}.
     */
    @Override
    public void selectAll() {
        for (int i = 0; i < managedList.getAdapter().getCount(); i++) {
            if (managedList.getAdapter().isEnabled(i)
                    && (onlyCheckClass == null || onlyCheckClass.isInstance(managedList.getItemAtPosition(i))))
                managedList.setItemChecked(i, true);
        }
    }

    /**
     * Implements the {@link SelectionModificationSpinner}'s select finished command by checking each (enabled) item
     * that represents something that is {@link Finishable} and indeed is finished;
     */
    @Override
    public void selectFinished() {
        for (int i = 0; i < managedList.getAdapter().getCount(); i++) {
            if (managedList.getAdapter().isEnabled(i)
                    && (onlyCheckClass == null || onlyCheckClass.isInstance(managedList.getItemAtPosition(i)))
                    && managedList.getItemAtPosition(i) instanceof Finishable)
                managedList.setItemChecked(i, ((Finishable) managedList.getItemAtPosition(i)).isFinished());
        }
    }

}
